/*
 * Copyright 2013 Agustina Martinez-Garcia
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
package uk.ljmu.qudex.qudexservlet.queryutils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.igfay.jfig.JFig;
import org.igfay.jfig.JFigException;

public class SparqlConstants {
	private static Logger log = Logger.getLogger(SparqlConstants.class);
	private static final String PROPERTY_DELIMITER = "$";
	
	public static final String FILE_PREFIX = "document";
	public static final String CATEGORY_PREFIX = "category";
	public static final String COLLECTION_PREFIX = "collection";
	
	public static final String COL_PREFIX = "qudex";
	public static final String SPARQL_JSON = "json";
	public static final String DEFAULT_FEDORA_SERVER = "http://localhost:8080/fedora";
    
	// PREDEFINED QUERIES
	/*
	 * Gets all the collections in a given graph (including sub-collections)
	 */
	public static String GET_ALL_COLLECTIONS;
	public static String GET_ALL_COLLECTIONS_MOD;
	/*
	 * Gets all the ROOT collections in a given graph (not including
	 * sub-collections)
	 */
	public static String GET_ALL_ROOT_COLLECTIONS;
	/*
	 * For a given collection, in a particular graph gets all the
	 * sub-collections depending on itself
	 */
	public static String GET_COLLECTION_TREE;
	/*
	 * Gets al the Document Objects per collection
	 */
	public static String GET_DOCUMENTS_BY_COLLECTION;
    
	public static String GET_DOCUMENTS_BY_COLLECTION_PERF;
	/*
	 * Gets all the categories included in a given collection
	 */
	public static String GET_CATEGORIES_BY_COLLECTION;
    
	static {
		StringBuilder sb = new StringBuilder();
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append(" PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append(" PREFIX ddi2: <http://example.org/ddi2/>");
		sb.append(" PREFIX qudex: <http://example.org/qudex/>");
		sb.append(" PREFIX ex: <http://example.org/>");
		sb.append(" PREFIX fn: <http://www.w3.org/2005/xpath-functions/#>");
		sb.append(" PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>");
		sb.append(" PREFIX skos: <http://www.w3.org/2004/02/skos/core/>");
		sb.append(" SELECT ?id ?identifier ?label ?desc ?lang ?subj ?cov ?rights ?contr ?dc_type ?creator ?date ?pub ?lat ?loc ?samp ?univ ?time ?dataCol ?colMode ?note ?type ?relation");
		sb.append(" FROM $GRAPH$");
		sb.append(" WHERE {");
		sb.append(" {");
		sb.append(" 	?col a ?rtype;");
		sb.append(" 		dc:identifier ?id;");
		sb.append(" 		dc:title ?label;");
		sb.append(" 		rel:isPartOf+ <info:fedora/$COL_ID$>.");
		sb.append(" 	optional {?col rel:isPartOf ?col1.}");
		sb.append(" 	optional {?col dc:description ?desc.}");
		sb.append(" 	optional {?col dc:language ?lang.}");
		sb.append(" 	optional {?col dc:subject ?subj.}");
		sb.append(" 	optional {?col dc:coverage ?cov.}");
		sb.append(" 	optional {?col dc:rights ?rights.}");
		sb.append(" 	optional {?col dc:contributor ?contr.}");
		sb.append(" 	optional {?col dc:type ?dc_type.}");
		sb.append(" 	optional {?col dc:creator ?creator.}");
		sb.append(" 	optional {?col dc:date ?date.}");
		sb.append(" 	optional {?col dc:publisher ?pub.}");
		sb.append(" 	optional {?col geo:lat_long ?lat.}");
		sb.append(" 	optional {?col geo:location ?loc.}");
		sb.append(" 	optional {?col ddi2:samplingProcedure ?samp.}");
		sb.append(" 	optional {?col ddi2:universe ?univ.}");
		sb.append(" 	optional {?col ddi2:timeMethod ?time.}");
		sb.append(" 	optional {?col ddi2:dataCollector ?dataCol.}");
		sb.append(" 	optional {?col ddi2:collectionMode ?colMode.}");
		sb.append(" 	optional {?col skos:note ?note.}");
		sb.append(" 	FILTER ((?rtype = dcmitype:Collection) && regex(str(?id), '.*$COL_PREFIX$.*'))");
		sb.append(" 	} UNION");
		sb.append(" 	{");
		sb.append(" 	<info:fedora/$COL_ID$> a ?rtype;");
		sb.append(" 		dc:identifier ?id;");
		sb.append(" 		dc:title ?label.");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:description ?desc.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:language ?lang.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:subject ?subj.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:coverage ?cov.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:rights ?rights.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:contributor ?contr.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:type ?dc_type.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:creator ?creator.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:date ?date.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> dc:publisher ?pub.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> geo:lat_long ?lat.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> geo:location ?loc.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> ddi2:samplingProcedure ?samp.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> ddi2:universe ?univ.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> ddi2:timeMethod ?time.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> ddi2:dataCollector ?dataCol.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> ddi2:collectionMode ?colMode.}");
		sb.append(" 	optional {<info:fedora/$COL_ID$> skos:note ?note.}");
		sb.append(" 	}");
		sb.append(" 	LET (?type := fn:substring(str(?rtype), 29)).");
		sb.append(" 	LET (?relation := fn:substring(str(?col1), 13)).");
		sb.append("     LET (?identifier := ?id).");
		sb.append(" }");
		GET_ALL_COLLECTIONS_MOD = sb.toString();
		sb.delete(0, GET_ALL_COLLECTIONS_MOD.length());
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append("	PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append("	PREFIX ddi2: <http://example.org/ddi2/>");
		sb.append("	PREFIX qudex: <http://example.org/qudex/>");
		sb.append(" PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>");
		sb.append(" PREFIX skos: <http://www.w3.org/2004/02/skos/core/>");
		sb.append("	SELECT ?id ?identifier ?label ?desc ?lang ?subj ?cov ?rights ?contr "
                  + "?dc_type ?creator ?date ?pub ?lat ?loc ?samp ?univ "
                  + "?time ?dataCol ?colMode ?note ?type ?relation");
		sb.append("	FROM $GRAPH$");
		sb.append("	WHERE {");
		sb.append("	?col a ?rtype;");
		sb.append("	dc:identifier ?id;");
		sb.append("	dc:title ?label.");
		sb.append("	optional {?col rel:isPartOf <info:fedora/$COL_ID$>.}");
		sb.append("	optional {?col dc:description ?desc.}");
		sb.append("	optional {?col dc:language ?lang.}");
		sb.append("	optional {?col dc:subject ?subj.}");
		sb.append("	optional {?col dc:coverage ?cov.}");
		sb.append("	optional {?col dc:rights ?rights.}");
		sb.append("	optional {?col dc:contributor ?contr.}");
		sb.append("	optional {?col dc:type ?dc_type.}");
		sb.append("	optional {?col dc:creator ?creator.}");
		sb.append("	optional {?col dc:date ?date.}");
		sb.append("	optional {?col dc:publisher ?pub.}");
		sb.append("	optional {?col geo:lat_long ?lat.}");
		sb.append("	optional {?col geo:location ?loc.}");
		sb.append("	optional {?col ddi2:samplingProcedure ?samp.}");
		sb.append("	optional {?col ddi2:universe ?univ.}");
		sb.append("	optional {?col ddi2:timeMethod ?time.}");
		sb.append("	optional {?col ddi2:dataCollector ?dataCol.}");
		sb.append("	optional {?col ddi2:collectionMode ?colMode.}");
		sb.append("	optional {?col skos:note ?note.}");
		sb.append("	FILTER ((?rtype = dcmitype:Collection) && regex(str(?id), '.*$COL_PREFIX$.*'))");
		// Prefixed values to extract the Type from the DCMI Types vocabulary,
		// i.e. Collection
		sb.append("	LET (?type := fn:substring(str(?rtype), 29)).");
		// Extract the prefix 'info:fedora' from the object identifier
		sb.append("	LET (?relation := fn:substring(str(?col1), 13)).");
		sb.append("	LET (?identifier := ?id).");
		sb.append("	}");
        
		GET_ALL_COLLECTIONS = sb.toString();
		sb.delete(0, GET_ALL_COLLECTIONS.length());
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append(" PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append(" SELECT DISTINCT * ");
		sb.append(" FROM $GRAPH$");
		sb.append(" WHERE {");
		sb.append(" { ?col a ?type;");
		sb.append(" dc:identifier ?id;");
		sb.append(" dc:title ?label;");
		sb.append(" rel:isPartOf+ <info:fedora/$COL_ID$>.");
		sb.append(" filter((str(?type) = str(dcmitype:Collection)) && (regex(?id, '.*$COL_PREFIX$.*')))}");
		sb.append(" UNION");
		sb.append(" {");
		sb.append(" <info:fedora/$COL_ID$> a ?type;");
		sb.append(" dc:identifier ?id;");
		sb.append(" dc:title ?label.");
		sb.append(" filter((str(?type) = str(dcmitype:Collection)))");
		sb.append(" }");
		sb.append(" }");
        
		GET_COLLECTION_TREE = sb.toString();
		sb.delete(0, GET_COLLECTION_TREE.length());
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append(" PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append(" PREFIX ddi2: <http://example.org/ddi2/>");
		sb.append(" PREFIX qudex: <http://example.org/qudex/>");
		sb.append(" PREFIX ex: <http://example.org/>");
		sb.append(" PREFIX fn: <http://www.w3.org/2005/xpath-functions/#>");
		sb.append(" PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#>");
		sb.append(" PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>");
		sb.append(" SELECT DISTINCT * ");
		sb.append(" FROM $GRAPH$");
		sb.append(" WHERE {");
		sb.append(" ?col a ?type;");
		sb.append(" dc:identifier ?id;");
		sb.append(" dc:title ?label.");
		sb.append(" filter(str(?type) = str(dcmitype:Collection))");
		sb.append(" optional{?col1 rel:isPartOf ?col.} FILTER(!bound(?col1)).");
		sb.append(" }");
        
		GET_ALL_ROOT_COLLECTIONS = sb.toString();
		sb.delete(0, GET_ALL_ROOT_COLLECTIONS.length());
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append(" PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append(" PREFIX ddi2: <http://example.org/ddi2/>");
		sb.append(" PREFIX qudex: <http://example.org/qudex/>");
		sb.append(" PREFIX ex: <http://example.org/>");
		sb.append(" PREFIX fn: <http://www.w3.org/2005/xpath-functions/#>");
		sb.append(" PREFIX fview: <info:fedora/fedora-system:def/view#>");
		sb.append(" PREFIX smf: <http://www.topquadrant.com/sparqlmotion/smf.html#smf:>");
		sb.append(" PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>");
        
		sb.append(" SELECT DISTINCT ?id ?label ?desc ?format ?lang ?subj ?cov ?dc_type ?creator ?date ?lat ?loc ?label_code ?labelCategory ?label_memo ?memo ?is_original ?type ?relation ?orig_ref ?source_ref ?hasAnnotation ?category ?isDocumentationOf ?isAnnotationOf ?isVersionOf ?isFormatOf ?isRelatedTo ?isReferencedBy");
		sb.append(" FROM $GRAPH$");
		sb.append(" WHERE {");
		sb.append(" ?file a ?rtype;");
		sb.append(" dc:identifier ?id;");
		sb.append(" dc:title ?label;");
		sb.append(" dc:format ?format;");
		sb.append(" dc:creator ?creator;");
   		sb.append(" fview:disseminates ?sref;");
		sb.append(" rel:isPartOf <info:fedora/$COL_ID$>.");
        sb.append(" FILTER ((?rtype = ex:File) && regex(str(?id), '.*qudex.*') && ((regex(str(?sref), '.*sourceReference.*', 'i') || (regex(str(?sref), '.*originalReference.*', 'i')))))");
   		sb.append(" optional {?file qudex:labelCode ?label_code.}");
        sb.append(" optional {?file qudex:isVersionOf ?isVersionOf.}");
		sb.append(" optional {?file qudex:isReferencedBy ?isReferencedBy.}");
   		sb.append(" optional {?file qudex:isDocumentationOf ?isDocumentationOf.}");
		sb.append(" optional {?file rel:isPartOf ?col1.}");
		sb.append(" optional {?file dc:language ?lang.}");
		sb.append(" optional {?file dc:subject ?subj.}");
		sb.append(" optional {?file dc:coverage ?cov.}");
		sb.append(" optional {?file dc:type ?dc_type.}");
		sb.append(" optional {?file dc:date ?date.}");
		sb.append(" optional {?file dc:description ?desc.}");
		sb.append(" optional {?file geo:location ?loc.}");
		sb.append(" optional {?file qudex:labelCategory ?label_cat.}");
		sb.append(" optional {?file qudex:category ?category.}");
		sb.append(" optional {?file qudex:labelMemo ?label_memo.}");
		sb.append(" optional {?file qudex:memo ?memo.}");
		sb.append(" optional {?file qudex:isOriginal ?is_original.}");
		sb.append(" optional {?file rel:hasAnnotation ?annotation.}");
		sb.append(" optional {?file rel:isAnnotationOf ?isAnnotationOf.}");
		sb.append(" optional {?file qudex:isRelatedTo ?isRelatedTo.}");
		sb.append(" optional {?file qudex:isFormatOf ?isFormatOf.}");
   		sb.append(" optional {?file geo:lat_long ?lat.}");
		sb.append(" LET (?type := fn:substring(str(?rtype), smf:lastIndexOf(str(?rtype),'/') + 2)).");
		sb.append(" LET (?relation := fn:substring(str(?col1), smf:lastIndexOf(str(?col1),'/') + 2)).");
		sb.append(" LET (?hasAnnotation := fn:substring(str(?annotation), smf:lastIndexOf(str(?annotation),'/') + 2)).");
		sb.append(" LET (?source_ref := fn:concat('$FEDORA_SERVER$/objects/', fn:concat(str(?id), '/datastreams/sourceReference/content'))).");
		sb.append(" LET (?orig_ref := fn:concat('$FEDORA_SERVER$/objects/', fn:concat(str(?id), '/datastreams/originalReference/content'))).");
		sb.append(" LET (?labelCategory := fn:substring(?label_cat, smf:lastIndexOf(?label_cat,':') +2)).");
		sb.append(" }");
        
		GET_DOCUMENTS_BY_COLLECTION = sb.toString();
		sb.delete(0, GET_DOCUMENTS_BY_COLLECTION.length());
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append(" PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append(" PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>");
		sb.append(" PREFIX ddi2: <http://example.org/ddi2/>");
		sb.append(" PREFIX qudex: <http://example.org/qudex/>");
		sb.append(" PREFIX fn: <http://www.w3.org/2005/xpath-functions/#>");
		sb.append("PREFIX smf: <http://www.topquadrant.com/sparqlmotion/smf.html#smf:>");
		sb.append(" SELECT ?id ?label ?isAnnotationOf ?cat_relation ?cat_parent_label ?type");
		sb.append(" FROM $GRAPH$");
		sb.append(" WHERE {");
		sb.append(" ?cat dc:identifier ?id;");
		sb.append(" rdfs:label ?cat_label;");
		sb.append(" rel:isAnnotationOf ?file;");
		sb.append(" rel:isPartOf ?col1.");
		sb.append(" FILTER (regex(str(?id), '.*$COL_PREFIX$.*') && (str(?col1) = 'info:fedora/$COL_ID$'))");
		sb.append(" LET (?cat_relation := fn:substring(str(?col1), smf:lastIndexOf(str(?col1),'/') + 2)).");
		sb.append(" LET (?isAnnotationOf := fn:substring(str(?file), smf:lastIndexOf(str(?file),'/') + 2)).");
		sb.append(" LET (?cat_parent_label := fn:substring(?cat_label, 0, smf:lastIndexOf(?cat_label,':')+1)).");
		sb.append(" LET (?label := fn:substring(?cat_label, smf:lastIndexOf(?cat_label,':') +2)).");
		sb.append(" LET (?type := 'Category').");
		sb.append(" }");
        
		GET_CATEGORIES_BY_COLLECTION = sb.toString();
		sb.delete(0, GET_CATEGORIES_BY_COLLECTION.length());
        
		sb.append("PREFIX dc: <http://purl.org/dc/elements/1.1/>");
		sb.append(" PREFIX rel: <info:fedora/fedora-system:def/relations-external#>");
		sb.append(" PREFIX dcmitype: <http://purl.org/dc/dcmitype/>");
		sb.append(" PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>");
		sb.append(" PREFIX ddi2: <http://example.org/ddi2/>");
		sb.append(" PREFIX qudex: <http://example.org/qudex/>");
		sb.append(" PREFIX ex: <http://example.org/>");
		sb.append(" PREFIX fview: <info:fedora/fedora-system:def/view#>");
		sb.append(" PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>");
		sb.append(" SELECT DISTINCT *");
		sb.append(" FROM $GRAPH$");
		sb.append(" WHERE {");
		sb.append(" ?s ?p ?o.");
		sb.append(" ?s rdf:type ex:File.");
		sb.append(" ?s rel:isPartOf <info:fedora/$COL_ID$>.");
		sb.append(" optional{?s fview:disseminates ?sref.}");
		sb.append(" FILTER (regex(str(?s), '.*$COL_PREFIX$.*') && (regex(str(?sref), '.*sourceReference.*', 'i')))");
		sb.append(" }");
		GET_DOCUMENTS_BY_COLLECTION_PERF = sb.toString();
		sb.delete(0, GET_DOCUMENTS_BY_COLLECTION_PERF.length());
	}
    
	// Query Formatting METHODS, using Regular expressions
	/**
	 *
	 * @param query
	 * @return modified Query
	 */
	public static String parseQuery(String query) {
		Pattern regexSubstitution;
        
		regexSubstitution = Pattern.compile("(.*?)(\\" + PROPERTY_DELIMITER
                                            + ")(.*?)(\\" + PROPERTY_DELIMITER + ")(.*?)$",
                                            Pattern.MULTILINE);
		String parameterName = null;
		String start = null;
		String rest = null;
		String replacementValue = null;
        
		// Loop through value while it has a symbolic reference
		Matcher matcher = regexSubstitution.matcher(query);
        
		while ((query.indexOf(PROPERTY_DELIMITER) >= 0) && matcher.find()) {
			// Get the values that we matched in our regular expression
			start = matcher.group(1);
			parameterName = matcher.group(3);
            
			rest = matcher.group(5);
			// Get the value for the section/key that we are referencing
            
			try {
				replacementValue = JFig.getInstance().getValue("mulgaraServer",
                                                               parameterName);
			} catch (JFigException e) {
				log.error("Error", e);
			}
            
			// Substitute the reference with the new value.
			if (replacementValue != null)
				query = start + replacementValue + rest;
            
			matcher = regexSubstitution.matcher(query);
            
		}
        
		return query;
	}
    
	/**
	 * Parses a pre-defined query and substitutes the wildcard parameter
	 * included in the query with the input value
	 *
	 * @param query
	 *            string query to be parsed
	 * @param paramElemName
	 *            parameter name to find in the query
	 * @param parameterValue
	 *            value of the wildcard to find in the query
	 * @return modified Query
	 */
	public static String parseQuery(String query, String paramElemName,
                                    String parameterValue) {
		Pattern regexSubstitution;
        
		regexSubstitution = Pattern.compile("(.*?)(\\" + PROPERTY_DELIMITER
                                            + ")(.*?)(\\" + PROPERTY_DELIMITER + ")(.*?)$",
                                            Pattern.MULTILINE);
		String parameterName = null;
		String start = null;
		String rest = null;
		String replacementValue = null;
        
		// Loop through value while it has a symbolic reference
		Matcher matcher = regexSubstitution.matcher(query.trim());
		String previousStart = "";
		boolean isRest = false;
		while ((query.indexOf(PROPERTY_DELIMITER) >= 0) && matcher.find()) {
			// Get the values that we matched in our regular expression
			if (isRest) {
				isRest = false;
				start = previousStart + matcher.group(1);
			} else
				start = matcher.group(1);
			parameterName = matcher.group(3);
            
			rest = matcher.group(5);
			if (!parameterName.equalsIgnoreCase(paramElemName)) {
				previousStart = start + "$" + parameterName + "$";
				isRest = true;
				matcher = regexSubstitution.matcher(rest);
				continue;
			}
			// Get the value for the section/key that we are referencing
            
			replacementValue = parameterValue;
            
			// Substitute the reference with the new value.
			if (replacementValue != null)
				query = start + replacementValue + rest;
            
			matcher = regexSubstitution.matcher(query);
		}
        
		return query;
	}
}
